﻿using System;

using System.Collections.Generic;
using Silvermoon.Controls.Classes;
using System.Drawing;
using Silvermoon.Controls.Interfaces;
using Silvermoon.Controls.Base;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Control that contains a control and a header and alows transition to another control.
    /// See also <seealso cref="T:Page"/>.
    /// </summary>
    public class PageContainer : EventControl, IScrollEventBehavior
    {
        #region fields

        private PageHeader header;
        private PageTransition pageTransition;
        private ControlCollection controls;
        private IPage page;

        #endregion
        #region ctor

        public PageContainer()
            : base()
        {
            controls = new ControlCollection(this);
            header = new PageHeader();
            pageTransition = new PageTransition();

            // first selector then header, so the header will hide the overlapping items without scissoring:
            controls.Add(pageTransition);
            controls.Add(header);
            pageTransition.Mode = PageTransitionMode.SwipeToLeft;
            header.Transition += new EventHandler<PageTransitionEventArgs>(OnTransition);
            header.PageChanged += new EventHandler(OnHeaderPageChanged);
        }

        #endregion
        #region props

        public PageHeader Header { get { return header; } }

        protected internal override IList<Control> SubControls { get { return controls; } }

        public IPage Page { get { return page; } set { if (page != value) SelectPage(value); Invalidate(InvalidationFlag.LayoutAppearance); } }

        #endregion
        #region method

        protected virtual void OnHeaderPageChanged(object sender, EventArgs e)
        {
            // the header can change it's Page, e.g. when the user click the back button, thus the PageContainer must also be actualized with the new value,
            // but without causing a trigger to start, hence we use "page" field instead of "Page" property:
            page = header.Page;
        }


        private bool focusOnTransitionComplete = true;

        public virtual void OnTransition(object sender, PageTransitionEventArgs e)
        {
            switch (e.State)
            {
                case PageTransitionState.Begin:
                    Screen.HideToolTip();
                    PrepareControl(page.Control);
                    focusOnTransitionComplete = page != null && page.Control != null && page.Control.HasFocus;
                    pageTransition.CurrentControl = e.TransitionPage.Control;
                    pageTransition.TransitionControl = e.TransitionPage.Control;
                    pageTransition.CurrentControl = e.Page.Control;
                    pageTransition.Mode = GetTransitionMode(e.Mode);
                    pageTransition.Level = 0;
                    pageTransition.IsAnimating = true;
                    IPage oldPage = page;
                    OnPageLoading(oldPage, header.Page);
                    page = header.Page;
                    //selector.Level = e.Mode == NavigationMode.GoForward ? 0 : PageSwapper.StartLevel;
                    break;

                case PageTransitionState.End:
                    pageTransition.IsAnimating = false;
                    page = header.Page;
                    pageTransition.CurrentControl = page != null ? page.Control : null;
                    pageTransition.Level = PageTransition.EndLevel;
                    if (focusOnTransitionComplete && page != null && page.Control != null) page.Control.Focus();
                    Invalidate(InvalidationFlag.LayoutAppearance | InvalidationFlag.Custom);
                    OnPageLoaded(header.TransitionPage, page);
                    break;


                case PageTransitionState.Progress:
                    int level = e.Progess * 10;
                    pageTransition.Level = level;

                    break;
            }
        }

        /// <summary>
        /// Prepares a control for transition. If the specified control or any of it's children support ITransitionBehavior, the transition is stopped.
        /// </summary>
        /// <param name="c"></param>
        private void PrepareControl(Control c)
        {
            if (c == null) return;
            ITransitionBehavior b = c as ITransitionBehavior;
            if (b != null) b.StopAnimations();
            if (c.SubControls != null)
            {
                foreach (var child in c.SubControls) PrepareControl(child);
            }
        }

        private PageTransitionMode GetTransitionMode(NavigationMode mode)
        {
            switch (mode)
            {
                case NavigationMode.GoBack: return PageTransitionMode.SwipeToLeft | PageTransitionMode.Fade;
                case NavigationMode.GoForward: return PageTransitionMode.SwipeToRight | PageTransitionMode.Fade;
                default: return PageTransitionMode.Fade;
            }
        }

        private void SelectPage(IPage page)
        {
            if (page == null) throw new ArgumentNullException("page");
            if (!Invalidated)
            {
                pageTransition.CurrentControl = page.Control;
                header.Page = page;
                this.page = page;
            }
            else
            {
                header.Page = page;
                pageTransition.CurrentControl = page.Control;
            }
        }

        protected override void OnLayout()
        {
            header.Bounds = new Rectangle(0, 0, Width, PageHeader.DefaultHeight);
            pageTransition.Bounds = new Rectangle(0, header.Bottom, Width, Height - header.Bottom);

            base.OnLayout();
        }

        /// <summary>
        /// Occurs after the page is complety loaded, and all transitions from the previous to the current page have completed.
        /// </summary>
        public virtual void OnPageLoaded(IPage oldPage, IPage newPage)
        {
            Control c = Page != null ? Page.Control : null;
            if (c != null) c.Focus();
            if (PageLoaded != null) PageLoaded(this, new PageEventArgs { NewPage = newPage, OldPage = oldPage });
        }


        /// <summary>
        /// Occurs before performing a transition to a new page.
        /// </summary>
        protected virtual void OnPageLoading(IPage oldPage, IPage newPage)
        {
            if (PageLoading != null) PageLoading(this, new PageEventArgs { NewPage = newPage, OldPage = oldPage });
        }



        public void GoBack()
        {
            header.GoBack();
        }

        #endregion
        #region events

        /// <summary>
        /// Occurs after the page is complety loaded, and all transitions from the previous to the current page have completed.
        /// </summary>
        public event EventHandler<PageEventArgs> PageLoaded;

        /// <summary>
        /// Occurs before performing a transition to a new page.
        /// </summary>
        public event EventHandler<PageEventArgs> PageLoading;



        #endregion
        #region IScrollEventBehavior Members

        public void OnScroll(ScrollEventArgs e)
        {
            if (!e.Handled)
            {
                int dx = e.Position.X - e.StartPosition.X;
                int ay = Math.Abs(e.Position.Y - e.StartPosition.Y);
                if (dx > Control.Scale(60) && (dx > ay))
                {
                    e.Handled = true;
                    e.Cancel = true;
                    GoBack();
                }
            }
        }


        public void OnBeginScrollData(BeginScrollEventArgs e)
        {
            e.Handled = true;
        }

        public void OnEndScrollData(EndScrollEventArgs e)
        {

        }

        #endregion
    }
}
